#include "simulationperspective.h"

#include "../dialogs/simulationcontroldialog.h"
#include "../documents/data/simgraphicdata.h"
#include "../editors/simulationcasenavigator.h"
#include "../mainwindow.h"
#include "../utils/documentmanager.h"
#include "../utils/modelvalidator.h"
#include "../utils/perspectivemanager.h"
#include "../utils/simulationrunner.h"
#include "../utils/undomanager.h"

#include <coremimetypes.h>
#include <dockwidget.h>
#include <editorarea.h>
#include <iconmanager.h>
#include <mimetypemanager.h>
#include <propertyeditor.h>

#include <QtCore/QSettings>
#include <QtGui/QAction>
#include <QtGui/QToolBar>
#include <QtGui/QUndoView>

using namespace core;
using namespace gui;
using namespace swp;

SimulationPerspective::SimulationPerspective(PerspectiveManager* manager,
    QWidget* parent) :
        QMainWindow(parent), IPerspective(),
        m_manager(manager),
        m_simCaseNavigatorDock(0),
        m_historyDock(0),
        m_propertyEditorDock(0),
        m_simCaseNavigator(0),
        m_history(0),
        m_propertyEditor(0),
        m_editorArea(0),
        m_simulationToolBar(0),
        m_viewSeparator(0),
        m_runSimulationAction(0),
        m_pauseSimulationAction(0),
        m_stopSimulationAction(0),
        m_defaultRepetitions(Settings::getInstance()->optionValue(
            "Simulation", "DefaultRepetitions").toInt()),
        m_defaultTimeout(Settings::getInstance()->optionValue(
            "Simulation", "DefaultTimeout").toInt()),
        m_connectedToPropertyEditor(),
        m_connectedForValidityChanges()
{
    IconManager* im = IconManager::getInstance();
    im->registerStyleIcon("SwpRunSimulation",
        ":/swp/icons/run_simulation_icon.png");
    im->registerStyleIcon("SwpPauseSimulation",
        ":/swp/icons/pause_simulation_icon.png");
    im->registerStyleIcon("SwpStopSimulation",
        ":/swp/icons/stop_simulation_icon.png");

    im->registerStyleIcon("SwpRunSimulationDialog",
        ":/swp/icons/run_sim_dia_icon.png");
    im->registerStyleIcon("SwpStopSimulationDialog",
        ":/swp/icons/stop_sim_dia_icon.png");
    im->registerStyleIcon("SwpViewReportDialog",
        ":/swp/icons/view_report_dia_icon.png");

    createActions();
    createCentralWidget();
    createDockWidgets();
    createToolBars();
    connectSlots();
}

SimulationPerspective::~SimulationPerspective()
{

}

QList<QAction* > SimulationPerspective::getStaticActions(
    const QString& menuId) const
{
    QList<QAction* > ret;

    return ret;
}

QList<QAction* > SimulationPerspective::getContextActions(
    const QString& menuId) const
{
    QList<QAction* > ret;

    if (menuId == VIEW_MENU)
    {
        ret.append(m_viewSeparator);
        ret.append(m_simCaseNavigatorDock->toggleViewAction());
    }
    else if (menuId == FINISHED)
    {
        m_simCaseNavigator->addWindowMenuActions();
    }

    return ret;
}

QList<QToolBar* > SimulationPerspective::getStaticToolBars() const
{
    QList<QToolBar* > ret;

    return ret;
}

QList<QToolBar* > SimulationPerspective::getContextToolBars() const
{
    QList<QToolBar* > ret;

    ret.append(m_simulationToolBar);

    return ret;
}

QList<QAction* > SimulationPerspective::getPerspectiveEnsuringActions() const
{
    QList<QAction* > ret;

    return ret;
}

IEditorArea* SimulationPerspective::getEditorArea() const
{
    return m_editorArea;
}

bool SimulationPerspective::canOpen(const MimeType& type) const
{
    bool ret =
        MimeTypeManager::getInstance()->mimeType(SIM_CASE_MIMETYPE) ==
            type;

    return ret;
}

void SimulationPerspective::handleIsCurrentChanged(bool current)
{
    if (current)
    {
        UndoManager::getInstance()->bindView(m_history);
    }
}

void SimulationPerspective::readSettings(QSettings* settings)
{
    const QStringList groups = settings->childGroups();

    if (groups.contains("SimulationPerspective"))
    {
        settings->beginGroup("SimulationPerspective");
        restoreState(settings->value("state").toByteArray());
        settings->endGroup();
    }
}

void SimulationPerspective::writeSettings(QSettings* settings) const
{
    settings->beginGroup("SimulationPerspective");
    settings->setValue("state", saveState());
    settings->endGroup();
}

void SimulationPerspective::setDefaultsForSimulationDialog(int repetitions,
    int timeout)
{
    if (repetitions != -1)
    {
        m_defaultRepetitions = repetitions;
    }
    if (timeout != -1)
    {
        m_defaultTimeout = timeout;
    }
}

void SimulationPerspective::createCentralWidget()
{
    m_editorArea = new EditorArea(this);

    setCentralWidget(m_editorArea);
}

void SimulationPerspective::createDockWidgets()
{
    m_simCaseNavigator = new SimulationCaseNavigator();

    m_history = new QUndoView();
    m_history->setAlternatingRowColors(true);

    m_propertyEditor = new PropertyEditor();

    m_simCaseNavigatorDock = new DockWidget(
        tr("Simulation Case Navigator"), this);
    m_simCaseNavigatorDock->setObjectName("SimulationCaseDockWidget");
    m_simCaseNavigatorDock->setWidget(m_simCaseNavigator);
    m_simCaseNavigatorDock->setAllowedAreas(Qt::LeftDockWidgetArea |
        Qt::RightDockWidgetArea);
    m_simCaseNavigatorDock->setFeatures(
        QDockWidget::DockWidgetClosable |
        QDockWidget::DockWidgetMovable);

    m_historyDock = new DockWidget(tr("Document History"), this);
    m_historyDock->setObjectName("HistoryDockWidget");
    m_historyDock->setWidget(m_history);
    m_historyDock->setFeatures(
        QDockWidget::DockWidgetClosable |
        QDockWidget::DockWidgetMovable);
    m_historyDock->setMinimumWidth(130);

    m_propertyEditorDock = new DockWidget(tr("Properties"), this);
    m_propertyEditorDock->setObjectName("PropertiesDockWidget");
    m_propertyEditorDock->setWidget(m_propertyEditor);
    m_propertyEditorDock->setFeatures(
        QDockWidget::DockWidgetClosable |
        QDockWidget::DockWidgetMovable);
    m_propertyEditorDock->setMinimumWidth(150);

    addDockWidget(Qt::LeftDockWidgetArea, m_simCaseNavigatorDock);
    addDockWidget(Qt::RightDockWidgetArea, m_historyDock);
    addDockWidget(Qt::LeftDockWidgetArea, m_propertyEditorDock);
}

void SimulationPerspective::createActions()
{
    m_viewSeparator = new QAction(this);
    m_viewSeparator->setSeparator(true);

    IconManager* im = IconManager::getInstance();

    m_runSimulationAction = new QAction(QObject::tr("Run simulation"),
        this);
    m_runSimulationAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R));
    if (im->hasIcon("SwpRunSimulation"))
    {
        m_runSimulationAction->setIcon(im->icon("SwpRunSimulation"));
    }

    m_pauseSimulationAction = new QAction(QObject::tr("Pause simulation"),
        this);
    if (im->hasIcon("SwpPauseSimulation"))
    {
        m_pauseSimulationAction->setIcon(im->icon("SwpPauseSimulation"));
    }

    m_stopSimulationAction = new QAction(QObject::tr("Stop simulation"),
        this);
    if (im->hasIcon("SwpStopSimulation"))
    {
        m_stopSimulationAction->setIcon(im->icon("SwpStopSimulation"));
    }
}

void SimulationPerspective::createToolBars()
{
    m_simulationToolBar = new QToolBar(QObject::tr("Simulation"), this);
    m_simulationToolBar->setObjectName("SimulationToolBar");

    m_simCaseNavigator->addToolBarActions(m_simulationToolBar);

    m_simulationToolBar->addAction(m_runSimulationAction);
    //m_simulationToolBar->addAction(m_pauseSimulationAction);
    //m_simulationToolBar->addAction(m_stopSimulationAction);
}

void SimulationPerspective::connectSlots()
{
    DocumentManager* dm = DocumentManager::getInstance();

    connect(dm, SIGNAL(projectOpened(QSharedPointer<core::IDocument>)),
        m_simCaseNavigator, SLOT(open(QSharedPointer<core::IDocument>)));
    connect(dm, SIGNAL(projectCreated(QSharedPointer<core::IDocument>)),
        m_simCaseNavigator, SLOT(open(QSharedPointer<core::IDocument>)));
    connect(dm, SIGNAL(documentClosed(QSharedPointer<core::IDocument>)),
        m_simCaseNavigator, SLOT(close(QSharedPointer<core::IDocument>)));
    connect(dm, SIGNAL(documentAdded(
        QSharedPointer<core::IDocument>, QSharedPointer<core::IDocument>)),
            m_simCaseNavigator, SLOT(add(
                QSharedPointer<core::IDocument>,
                QSharedPointer<core::IDocument>)));
    connect(dm, SIGNAL(documentRemoved(
        QSharedPointer<core::IDocument>, QSharedPointer<core::IDocument>)),
            m_simCaseNavigator, SLOT(remove(
                QSharedPointer<core::IDocument>,
                QSharedPointer<core::IDocument>)));
    connect(dm, SIGNAL(documentRemoved(
        QSharedPointer<core::IDocument>)),
            m_simCaseNavigator, SLOT(remove(
                QSharedPointer<core::IDocument>)));

    connect(m_simCaseNavigator, SIGNAL(aboutToOpen(
        QSharedPointer<core::IDocument>)),
            dm, SLOT(openDocument(
                QSharedPointer<core::IDocument>)));
    connect(m_simCaseNavigator, SIGNAL(aboutToSave(
        QSharedPointer<core::IDocument>)),
            dm, SLOT(saveDocument(
                QSharedPointer<core::IDocument>)));
    connect(m_simCaseNavigator, SIGNAL(aboutToClose(
        QSharedPointer<core::IDocument>)),
            dm, SLOT(closeDocument(
                QSharedPointer<core::IDocument>)));
    connect(m_simCaseNavigator, SIGNAL(aboutToAdd(
        QSharedPointer<core::IDocument>, QList<core::MimeType>, uint, bool)),
            dm, SLOT(addDocument(
                QSharedPointer<core::IDocument>, QList<core::MimeType>, uint,
                    bool)));
    connect(m_simCaseNavigator, SIGNAL(aboutToRemove(
        QSharedPointer<core::IDocument>, QSharedPointer<core::IDocument>)),
            dm, SLOT(removeDocument(
                QSharedPointer<core::IDocument>,
                QSharedPointer<core::IDocument>)));
    connect(m_simCaseNavigator, SIGNAL(aboutToDelete(
        QSharedPointer<core::IDocument>)),
            dm, SLOT(deleteDocument(
                QSharedPointer<core::IDocument>)));

    connect(m_editorArea, SIGNAL(editorOpened(QSharedPointer<core::IEditor>)),
        this, SLOT(connectPropertyEditor(
            QSharedPointer<core::IEditor>)));
    connect(dm, SIGNAL(documentClosed(QSharedPointer<core::IDocument>)),
        this, SLOT(disconnectPropertyEditor(
            QSharedPointer<core::IDocument>)));
    connect(m_editorArea, SIGNAL(editorOpened(QSharedPointer<core::IEditor>)),
        this, SLOT(connectValidityChecker(
            QSharedPointer<core::IEditor>)));
    connect(dm, SIGNAL(documentClosed(QSharedPointer<core::IDocument>)),
        this, SLOT(disconnectValidityChecker(
            QSharedPointer<core::IDocument>)));
    connect(m_editorArea,
        SIGNAL(currentChanged(QSharedPointer<core::IEditor>)),
        this, SLOT(resetPropertyEditor(QSharedPointer<core::IEditor>)));

    connect(ModelValidator::getInstance(),
        SIGNAL(highlightDocumentRequest(QSharedPointer<core::IDocument>)),
        this,
        SLOT(highlightDocument(QSharedPointer<core::IDocument>)));

    MainWindow* win = MainWindow::getInstance();

    connect(m_editorArea, SIGNAL(
        currentChanged(QSharedPointer<core::IEditor>)),
            win, SLOT(
                handleEditorChange(QSharedPointer<core::IEditor>)));
    connect(m_editorArea, SIGNAL(
        currentChanged(QSharedPointer<core::IEditor>)),
            this, SLOT(
                handleEditorChange(QSharedPointer<core::IEditor>)));
    connect(m_editorArea, SIGNAL(
        aboutToClose(QSharedPointer<core::IEditor>)),
            dm, SLOT(
                closeDocument(QSharedPointer<core::IEditor>)));
    connect(dm, SIGNAL(
        documentClosed(QSharedPointer<core::IEditor>)),
            m_editorArea, SLOT(
                confirmClose(QSharedPointer<core::IEditor>)));
    connect(m_editorArea, SIGNAL(aboutToSave(
        QSharedPointer<core::IDocument>)), dm, SLOT(
            saveDocument(QSharedPointer<core::IDocument>)));

    win->registerForSaveNotification(this, m_editorArea);
    win->registerForSaveNotification(this, m_simCaseNavigator);

    connect(m_runSimulationAction, SIGNAL(triggered()),
        this, SLOT(runSimulation()));
}

void SimulationPerspective::connectPropertyEditor(
    const QSharedPointer<core::IEditor>& editor)
{
    if (editor->document()->data()->type() == IDocumentData::GraphicModel)
    {
        GraphicData* data = dynamic_cast<GraphicData*>(
            editor->document()->data());

        Q_ASSERT(data != 0); //bad data type

        connect(data, SIGNAL(editorItemSelectionChanged(
            core::IGraphicEditorItem*)),
                this, SLOT(notifyPropertyEditor(core::IGraphicEditorItem*)));

        m_connectedToPropertyEditor.append(data);
    }
}

void SimulationPerspective::disconnectPropertyEditor(
    const QSharedPointer<core::IDocument>& document)
{
    if (document->data()->type() == IDocumentData::GraphicModel)
    {
        GraphicData* data = dynamic_cast<GraphicData*>(document->data());

        Q_ASSERT(data != 0); //bad data type

        if (m_connectedToPropertyEditor.contains(data))
        {
            disconnect(data, SIGNAL(editorItemSelectionChanged(
                core::IGraphicEditorItem*)),
                    this, SLOT(notifyPropertyEditor(
                        core::IGraphicEditorItem*)));

            m_propertyEditor->propertyObjectChanged(0);

            m_connectedToPropertyEditor.removeAll(data);
        }
    }
}

void SimulationPerspective::connectValidityChecker(
    const QSharedPointer<IEditor>& editor)
{
    connect(editor->document().data(), SIGNAL(validityChanged(bool)),
        this, SLOT(enableSimActions(bool)));

    m_connectedForValidityChanges.append(editor->document());
}

void SimulationPerspective::disconnectValidityChecker(
    const QSharedPointer<IDocument>& document)
{
    if (m_connectedForValidityChanges.contains(document))
    {
        disconnect(document.data(), SIGNAL(validityChanged(bool)),
            this, SLOT(enableSimActions(bool)));
        m_connectedForValidityChanges.removeAll(document);
    }
}

void SimulationPerspective::notifyPropertyEditor(
    core::IGraphicEditorItem* item)
{
    m_propertyEditor->propertyObjectChanged(item);
}

void SimulationPerspective::resetPropertyEditor(
    const QSharedPointer<IEditor>& editor)
{
    if (!editor.isNull())
    {
        const QSharedPointer<IDocument> document = editor->document();

        if (document->data()->type() == IDocumentData::GraphicModel)
        {
            GraphicData* data = dynamic_cast<GraphicData*>(document->data());

            Q_ASSERT(data != 0); //bad data type

            const QList<IGraphicEditorItem* > selected =
                data->selectedGraphicItems();

            if (selected.size() > 0)
            {
                m_propertyEditor->propertyObjectChanged(selected.last());
            }
            else
            {
                m_propertyEditor->propertyObjectChanged(0);
            }
        }
        else
        {
            m_propertyEditor->propertyObjectChanged(0);
        }
    }
    else
    {
        m_propertyEditor->propertyObjectChanged(0);
    }
}

void SimulationPerspective::handleEditorChange(
    const QSharedPointer<IEditor>& editor)
{
    if (!editor.isNull())
    {
        const bool isValid = editor->document()->isValid();

        m_runSimulationAction->setEnabled(isValid);
        m_pauseSimulationAction->setEnabled(isValid);
        m_stopSimulationAction->setEnabled(isValid);
    }
    else
    {
        m_runSimulationAction->setEnabled(false);
        m_pauseSimulationAction->setEnabled(false);
        m_stopSimulationAction->setEnabled(false);
    }
}

void SimulationPerspective::highlightDocument(
    const QSharedPointer<IDocument>& document)
{
    m_simCaseNavigator->tryToSelectDocument(document);

    if (!document->isFuture() && canOpen(document->mimeType()))
    {
        if (m_manager != 0)
        {
            m_manager->ensurePerspectiveRequest(this);
        }

        m_editorArea->openEditor(document);
    }
}

void SimulationPerspective::enableSimActions(bool enable)
{
    IDocument* _sender = qobject_cast<IDocument*>(sender());

    if (m_editorArea->currentEditor() != 0 ||
        m_editorArea->currentEditor()->document().data() == _sender)
    {
        m_runSimulationAction->setEnabled(enable);
        m_pauseSimulationAction->setEnabled(enable);
        m_stopSimulationAction->setEnabled(enable);
    }
}

void SimulationPerspective::runSimulation()
{
    if (m_editorArea->currentEditor() != 0)
    {
        SimGraphicData* data = dynamic_cast<SimGraphicData* >(
            m_editorArea->currentEditor()->document()->data());

        QSharedPointer<SwpSimulationCaseDocument> document =
            qSharedPointerDynamicCast<SwpSimulationCaseDocument>(
                m_editorArea->currentEditor()->document());

        Q_ASSERT(document != 0 && data != 0); // bad data type

        if (m_editorArea->currentEditor()->document()->isValid())
        {
            if (data->isNetDirty())
            {
                data->updateNet();
            }

            SimulationControlDialog* dialog = new SimulationControlDialog(
                document, data->net(), m_defaultRepetitions, m_defaultTimeout,
                this);
            dialog->exec();

            delete dialog;
        }
    }
}
